﻿/* Open source distributed without warranties of any kind. */

namespace Transcriber.Plugins
{
    #region Using
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Text.RegularExpressions;
    using Transcriber.Api;
    #endregion

    /// <summary>
    /// <see cref="IRuleEvaluator"/> that converts sequences
    /// of digits to numeric tengwar.
    /// </summary>
    public abstract class NumericTengwarEvaluator : Disposable, IRuleEvaluator
    {
        private static char[] sDigitMap = new char[]
        {
            'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 'ø', 'ù', 'ú', 'û'
        };

        private static char[] sDuodecimalIndicator = new char[]
        {
            'É', 'É', 'È', 'È', 'É', 'È', 'È', 'É', 'É', 'È', 'É', 'É'
        };

        private bool mDuodecimal;
        private UnmatchedAction mUnmatchedAction = UnmatchedAction.Omit;
        private string mReplacementText;

        /// <summary>
        /// Gets or sets the unmatched action.
        /// </summary>
        /// <value>The unmatched action.</value>
        public UnmatchedAction UnmatchedAction
        {
            get { return this.mUnmatchedAction; }
            set { this.mUnmatchedAction = value; }
        }

        /// <summary>
        /// Gets or sets the replacement text.
        /// </summary>
        /// <value>The replacement text.</value>
        public string ReplacementText
        {
            get { return this.mReplacementText; }
            set { this.mReplacementText = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="NumericTengwarEvaluator"/> is duodecimal.
        /// </summary>
        /// <value><c>true</c> if duodecimal; otherwise, <c>false</c>.</value>
        public bool Duodecimal
        {
            get { return this.mDuodecimal; }
            set { this.mDuodecimal = value; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NumericTengwarEvaluator"/> class.
        /// </summary>
        public NumericTengwarEvaluator()
            : this(false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NumericTengwarEvaluator"/> class.
        /// </summary>
        /// <param name="duodecimal">if set to <c>true</c> [duodecimal].</param>
        public NumericTengwarEvaluator(bool duodecimal)
            : this(duodecimal, UnmatchedAction.Omit)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NumericTengwarEvaluator"/> class.
        /// </summary>
        /// <param name="duodecimal">if set to <c>true</c> [duodecimal].</param>
        /// <param name="unmatchedAction">The unmatched action.</param>
        public NumericTengwarEvaluator(bool duodecimal, UnmatchedAction unmatchedAction)
            : this(duodecimal, unmatchedAction, "¤")
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NumericTengwarEvaluator"/> class.
        /// </summary>
        /// <param name="duodecimal">if set to <c>true</c> [duodecimal].</param>
        /// <param name="unmatchedAction">The unmatched action.</param>
        /// <param name="replacementText">The replacement text.</param>
        public NumericTengwarEvaluator(bool duodecimal, UnmatchedAction unmatchedAction, string replacementText)
        {
            this.mDuodecimal = duodecimal;
            this.mUnmatchedAction = unmatchedAction;
            this.mReplacementText = replacementText;
        }

        /// <summary>
        /// Evaluates the specified match.
        /// </summary>
        /// <param name="match">The match.</param>
        /// <returns>The replacement text for this match.</returns>
        public string Evaluate(Match match)
        {
            List<string> strings = new List<string>();

            foreach (char c in match.Value.ToLower())
            {
                if (this.IsDigit(c))
                {
                    strings.Add(this.Transcribe(c));
                    continue;
                }

                switch (this.mUnmatchedAction)
                {
                    case Plugins.UnmatchedAction.EmitOriginal:
                        strings.Add(Char.ToString(c));
                        break;

                    case Plugins.UnmatchedAction.EmitReplacement:
                        strings.Add(this.mReplacementText);
                        break;

                    case Plugins.UnmatchedAction.Omit:
                        break;

                    default:
                        throw new InvalidOperationException(string.Format("Unsupported UnmatchedAction value: {0}", this.mUnmatchedAction));
                }
            }

            StringBuilder builder = new StringBuilder();

            for (int i = (strings.Count - 1); i >= 0; --i)
            {
                builder.Append(strings[i]);
            }

            return builder.ToString();
        }

        private string Transcribe(char c)
        {
            if (Char.IsDigit(c))
            {
                StringBuilder builder = new StringBuilder();
                int index = (int)Char.GetNumericValue(c);
                builder.Append(NumericTengwarEvaluator.sDigitMap[index]);

                if (this.mDuodecimal)
                {
                    builder.Append(NumericTengwarEvaluator.sDuodecimalIndicator[index]);
                }

                return builder.ToString();
            }
            else if (!this.mDuodecimal)
            {
                throw new InvalidOperationException(string.Format("Decimal digit requied: {0}", c));
            }
            else if ('a' == c)
            {
                StringBuilder builder = new StringBuilder();
                builder.Append(NumericTengwarEvaluator.sDigitMap[10]);
                builder.Append(NumericTengwarEvaluator.sDuodecimalIndicator[10]);
                return builder.ToString();
            }
            else if ('b' == c)
            {
                StringBuilder builder = new StringBuilder();
                builder.Append(NumericTengwarEvaluator.sDigitMap[11]);
                builder.Append(NumericTengwarEvaluator.sDuodecimalIndicator[11]);
                return builder.ToString();
            }
            else
            {
                throw new InvalidOperationException(string.Format("Duodecimal digit required: {0}", c));
            }
        }

        private bool IsDigit(char c)
        {
            return (Char.IsDigit(c) || (this.mDuodecimal && (('a' == c) || ('b' == c))));
        }
    }
}
